home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_playerstate.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  10.0 KB  |  376 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_playerstate.c -- this file acts on changes in a new playerState_t
  4. // With normal play, this will be done after local prediction, but when
  5. // following another player or playing back a demo, it will be checked
  6. // when the snapshot transitions like all the other entities
  7.  
  8. #include "cg_local.h"
  9.  
  10. /*
  11. ==============
  12. CG_CheckAmmo
  13.  
  14. If the ammo has gone low enough to generate the warning, play a sound
  15. ==============
  16. */
  17. void CG_CheckAmmo( void ) {
  18.     int        i;
  19.     int        total;
  20.     int        previous;
  21.     int        weapons;
  22.  
  23.     // see about how many seconds of ammo we have remaining
  24.     weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
  25.     total = 0;
  26.     for ( i = WP_MACHINEGUN ; i < WP_NUM_WEAPONS ; i++ ) {
  27.         if ( ! ( weapons & ( 1 << i ) ) ) {
  28.             continue;
  29.         }
  30.         switch ( i ) {
  31.         case WP_ROCKET_LAUNCHER:
  32.         case WP_GRENADE_LAUNCHER:
  33.         case WP_RAILGUN:
  34.         case WP_SHOTGUN:
  35.             total += cg.snap->ps.ammo[i] * 1000;
  36.             break;
  37.         default:
  38.             total += cg.snap->ps.ammo[i] * 200;
  39.             break;
  40.         }
  41.         if ( total >= 5000 ) {
  42.             cg.lowAmmoWarning = 0;
  43.             return;
  44.         }
  45.     }
  46.  
  47.     previous = cg.lowAmmoWarning;
  48.  
  49.     if ( total == 0 ) {
  50.         cg.lowAmmoWarning = 2;
  51.     } else {
  52.         cg.lowAmmoWarning = 1;
  53.     }
  54.  
  55.     // play a sound on transitions
  56.     if ( cg.lowAmmoWarning != previous ) {
  57.         trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND );
  58.     }
  59. }
  60.  
  61. /*
  62. ==============
  63. CG_DamageFeedback
  64. ==============
  65. */
  66. void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) {
  67.     float        left, front, up;
  68.     float        kick;
  69.     int            health;
  70.     float        scale;
  71.     vec3_t        dir;
  72.     vec3_t        angles;
  73.     float        dist;
  74.     float        yaw, pitch;
  75.  
  76.     // show the attacking player's head and name in corner
  77.     cg.attackerTime = cg.time;
  78.  
  79.     // the lower on health you are, the greater the view kick will be
  80.     health = cg.snap->ps.stats[STAT_HEALTH];
  81.     if ( health < 40 ) {
  82.         scale = 1;
  83.     } else {
  84.         scale = 40.0 / health;
  85.     }
  86.     kick = damage * scale;
  87.  
  88.     if (kick < 5)
  89.         kick = 5;
  90.     if (kick > 10)
  91.         kick = 10;
  92.  
  93.     // if yaw and pitch are both 255, make the damage always centered (falling, etc)
  94.     if ( yawByte == 255 && pitchByte == 255 ) {
  95.         cg.damageX = 0;
  96.         cg.damageY = 0;
  97.         cg.v_dmg_roll = 0;
  98.         cg.v_dmg_pitch = -kick;
  99.     } else {
  100.         // positional
  101.         pitch = pitchByte / 255.0 * 360;
  102.         yaw = yawByte / 255.0 * 360;
  103.  
  104.         angles[PITCH] = pitch;
  105.         angles[YAW] = yaw;
  106.         angles[ROLL] = 0;
  107.  
  108.         AngleVectors( angles, dir, NULL, NULL );
  109.         VectorSubtract( vec3_origin, dir, dir );
  110.  
  111.         front = DotProduct (dir, cg.refdef.viewaxis[0] );
  112.         left = DotProduct (dir, cg.refdef.viewaxis[1] );
  113.         up = DotProduct (dir, cg.refdef.viewaxis[2] );
  114.  
  115.         dir[0] = front;
  116.         dir[1] = left;
  117.         dir[2] = 0;
  118.         dist = VectorLength( dir );
  119.         if ( dist < 0.1 ) {
  120.             dist = 0.1;
  121.         }
  122.  
  123.         cg.v_dmg_roll = kick * left;
  124.         
  125.         cg.v_dmg_pitch = -kick * front;
  126.  
  127.         if ( front <= 0.1 ) {
  128.             front = 0.1;
  129.         }
  130.         cg.damageX = -left / front;
  131.         cg.damageY = up / dist;
  132.     }
  133.  
  134.     // clamp the position
  135.     if ( cg.damageX > 1.0 ) {
  136.         cg.damageX = 1.0;
  137.     }
  138.     if ( cg.damageX < - 1.0 ) {
  139.         cg.damageX = -1.0;
  140.     }
  141.  
  142.     if ( cg.damageY > 1.0 ) {
  143.         cg.damageY = 1.0;
  144.     }
  145.     if ( cg.damageY < - 1.0 ) {
  146.         cg.damageY = -1.0;
  147.     }
  148.  
  149.     // don't let the screen flashes vary as much
  150.     if ( kick > 10 ) {
  151.         kick = 10;
  152.     }
  153.     cg.damageValue = kick;
  154.     cg.v_dmg_time = cg.time + DAMAGE_TIME;
  155.     cg.damageTime = cg.snap->serverTime;
  156. }
  157.  
  158.  
  159.  
  160.  
  161. /*
  162. ================
  163. CG_Respawn
  164.  
  165. A respawn happened this snapshot
  166. ================
  167. */
  168. void CG_Respawn( void ) {
  169.     // no error decay on player movement
  170.     cg.thisFrameTeleport = qtrue;
  171.  
  172.     // display weapons available
  173.     cg.weaponSelectTime = cg.time;
  174.  
  175.     // select the weapon the server says we are using
  176.     cg.weaponSelect = cg.snap->ps.weapon;
  177. }
  178.  
  179.  
  180. /*
  181. ==============
  182. CG_CheckPlayerstateEvents
  183.  
  184. ==============
  185. */
  186. void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
  187.     int            i;
  188.     int            event;
  189.     centity_t    *cent;
  190.  
  191.     if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) {
  192.         cent = &cg_entities[ ps->clientNum ];
  193.         cent->currentState.event = ps->externalEvent;
  194.         cent->currentState.eventParm = ps->externalEventParm;
  195.         CG_EntityEvent( cent, cent->lerpOrigin );
  196.     }
  197.  
  198.     cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ];
  199.  
  200.     for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
  201.         if ( ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]
  202.             || i >= ops->eventSequence ) {
  203.             event = ps->events[ i & (MAX_PS_EVENTS-1) ];
  204.  
  205.             cent->currentState.event = event;
  206.             cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
  207.             CG_EntityEvent( cent, cent->lerpOrigin );
  208.         }
  209.     }
  210. }
  211.  
  212. /*
  213. ==================
  214. CG_CheckLocalSounds
  215. ==================
  216. */
  217. void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
  218.     int            highScore;
  219.  
  220.     // hit changes
  221.     if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) {
  222.         trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
  223.     } else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) {
  224.         trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND );
  225.     }
  226.  
  227.     // health changes of more than -1 should make pain sounds
  228.     if ( ps->stats[STAT_HEALTH] < ops->stats[STAT_HEALTH] - 1 ) {
  229.         if ( ps->stats[STAT_HEALTH] > 0 ) {
  230.             CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[STAT_HEALTH] );
  231.         }
  232.     }
  233.  
  234.  
  235.     // if we are going into the intermission, don't start any voices
  236.     if ( cg.intermissionStarted ) {
  237.         return;
  238.     }
  239.  
  240.     // reward sounds
  241.     if ( ps->persistant[PERS_REWARD_COUNT] > ops->persistant[PERS_REWARD_COUNT] ) {
  242.         switch ( ps->persistant[PERS_REWARD] ) {
  243.         case REWARD_IMPRESSIVE:
  244.             trap_S_StartLocalSound( cgs.media.impressiveSound, CHAN_ANNOUNCER );
  245.             cg.rewardTime = cg.time;
  246.             cg.rewardShader = cgs.media.medalImpressive;
  247.             cg.rewardCount = ps->persistant[PERS_IMPRESSIVE_COUNT];
  248.             break;
  249.         case REWARD_EXCELLENT:
  250.             trap_S_StartLocalSound( cgs.media.excellentSound, CHAN_ANNOUNCER );
  251.             cg.rewardTime = cg.time;
  252.             cg.rewardShader = cgs.media.medalExcellent;
  253.             cg.rewardCount = ps->persistant[PERS_EXCELLENT_COUNT];
  254.             break;
  255.         case REWARD_DENIED:
  256.             trap_S_StartLocalSound( cgs.media.deniedSound, CHAN_ANNOUNCER );
  257.             break;
  258.         case REWARD_GAUNTLET:
  259.             trap_S_StartLocalSound( cgs.media.humiliationSound, CHAN_ANNOUNCER );
  260.             // if we are the killer and not the killee, show the award
  261.             if ( ps->stats[STAT_HEALTH] ) {
  262.                 cg.rewardTime = cg.time;
  263.                 cg.rewardShader = cgs.media.medalGauntlet;
  264.                 cg.rewardCount = ps->persistant[PERS_GAUNTLET_FRAG_COUNT];
  265.             }
  266.             break;
  267.         default:
  268.             CG_Error( "Bad reward_t" );
  269.         }
  270.     } else {
  271.         // lead changes (only if no reward)
  272.         if ( !cg.warmup ) {
  273.             // never play lead changes during warmup
  274.             if ( ps->persistant[PERS_RANK] != ops->persistant[PERS_RANK] ) {
  275.                 if ( cgs.gametype >= GT_TEAM ) {
  276.                     if ( ps->persistant[PERS_RANK] == 2 ) {
  277.                         trap_S_StartLocalSound( cgs.media.teamsTiedSound, CHAN_ANNOUNCER );
  278.                     } else if (  ps->persistant[PERS_RANK] == 0 ) {
  279.                         trap_S_StartLocalSound( cgs.media.redLeadsSound, CHAN_ANNOUNCER );
  280.                     } else if ( ps->persistant[PERS_RANK] == 1 ) {
  281.                         trap_S_StartLocalSound( cgs.media.blueLeadsSound, CHAN_ANNOUNCER );
  282.                     }
  283.                 } else {
  284.                     if (  ps->persistant[PERS_RANK] == 0 ) {
  285.                         trap_S_StartLocalSound( cgs.media.takenLeadSound, CHAN_ANNOUNCER );
  286.                     } else if ( ps->persistant[PERS_RANK] == RANK_TIED_FLAG ) {
  287.                         trap_S_StartLocalSound( cgs.media.tiedLeadSound, CHAN_ANNOUNCER );
  288.                     } else if ( ( ops->persistant[PERS_RANK] & ~RANK_TIED_FLAG ) == 0 ) {
  289.                         trap_S_StartLocalSound( cgs.media.lostLeadSound, CHAN_ANNOUNCER );
  290.                     }
  291.                 }
  292.             }
  293.         }
  294.     }
  295.  
  296.     // timelimit warnings
  297.     if ( cgs.timelimit > 0 ) {
  298.         int        msec;
  299.  
  300.         msec = cg.time - cgs.levelStartTime;
  301.  
  302.         if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && msec > (cgs.timelimit - 5) * 60 * 1000 ) {
  303.             cg.timelimitWarnings |= 1;
  304.             trap_S_StartLocalSound( cgs.media.fiveMinuteSound, CHAN_ANNOUNCER );
  305.         }
  306.         if ( !( cg.timelimitWarnings & 2 ) && msec > (cgs.timelimit - 1) * 60 * 1000 ) {
  307.             cg.timelimitWarnings |= 2;
  308.             trap_S_StartLocalSound( cgs.media.oneMinuteSound, CHAN_ANNOUNCER );
  309.         }
  310.         if ( !( cg.timelimitWarnings & 4 ) && msec > ( cgs.timelimit * 60 + 2 ) * 1000 ) {
  311.             cg.timelimitWarnings |= 4;
  312.             trap_S_StartLocalSound( cgs.media.suddenDeathSound, CHAN_ANNOUNCER );
  313.         }
  314.     }
  315.  
  316.     // fraglimit warnings
  317.     if ( cgs.fraglimit > 0 && cgs.gametype != GT_CTF ) {
  318.         highScore = cgs.scores1;
  319.         if ( cgs.fraglimit > 3 && !( cg.fraglimitWarnings & 1 ) && highScore == (cgs.fraglimit - 3) ) {
  320.             cg.fraglimitWarnings |= 1;
  321.             trap_S_StartLocalSound( cgs.media.threeFragSound, CHAN_ANNOUNCER );
  322.         }
  323.         if ( cgs.fraglimit > 2 && !( cg.fraglimitWarnings & 2 ) && highScore == (cgs.fraglimit - 2) ) {
  324.             cg.fraglimitWarnings |= 2;
  325.             trap_S_StartLocalSound( cgs.media.twoFragSound, CHAN_ANNOUNCER );
  326.         }
  327.         if ( !( cg.fraglimitWarnings & 4 ) && highScore == (cgs.fraglimit - 1) ) {
  328.             cg.fraglimitWarnings |= 4;
  329.             trap_S_StartLocalSound( cgs.media.oneFragSound, CHAN_ANNOUNCER );
  330.         }
  331.     }
  332. }
  333.  
  334. /*
  335. ===============
  336. CG_TransitionPlayerState
  337.  
  338. ===============
  339. */
  340. void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) {
  341.     // check for changing follow mode
  342.     if ( ps->clientNum != ops->clientNum ) {
  343.         cg.thisFrameTeleport = qtrue;
  344.         // make sure we don't get any unwanted transition effects
  345.         *ops = *ps;
  346.     }
  347.  
  348.     // damage events (player is getting wounded)
  349.     if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) {
  350.         CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
  351.     }
  352.  
  353.     // respawning
  354.     if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) {
  355.         CG_Respawn();
  356.     }
  357.  
  358.     if ( cg.snap->ps.pm_type != PM_INTERMISSION 
  359.         && ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
  360.         CG_CheckLocalSounds( ps, ops );
  361.     }
  362.  
  363.     // check for going low on ammo
  364.     CG_CheckAmmo();
  365.  
  366.     // run events
  367.     CG_CheckPlayerstateEvents( ps, ops );
  368.  
  369.     // smooth the ducking viewheight change
  370.     if ( ps->viewheight != ops->viewheight ) {
  371.         cg.duckChange = ps->viewheight - ops->viewheight;
  372.         cg.duckTime = cg.time;
  373.     }
  374. }
  375.  
  376.